<template>
  <div class="zeus-popover" ref="popover">
    <div
      ref="contentWrapper"
      class="content-wrapper"
      :class="{ [`position-${position}`]: true }"
      v-if="visible"
    >
      <slot name="content"></slot>
    </div>

    <span ref="triggerWrapper" style="display: inline-block">
      <slot />
    </span>
  </div>
</template>

<script lang="ts">
import { ref, nextTick, onMounted, onBeforeUnmount } from 'vue'
export default {
  props: {
    position: {
      type: String,
      default: 'top',
      validor(value) {
        return ['top', 'bottom', 'left', 'right'].indexOf(value) >= 0
      },
    },
    trigger: {
      type: String,
      default: 'click',
      validor(value) {
        return ['click', 'hover'].indexOf(value) >= 0
      },
    },
  },
  setup(props) {
    let visible = ref(false)
    const popover = ref<HTMLDivElement>(null)
    const triggerWrapper = ref<HTMLDivElement>(null)
    const contentWrapper = ref<HTMLDivElement>(null)

    onMounted(() => {
      if (props.trigger === 'click') {
        popover.value.addEventListener('click', toggle)
      } else {
        popover.value.addEventListener('mouseenter', open)
        popover.value.addEventListener('mouseleave', close)
      }
    })
    onBeforeUnmount(() => {
      if (props.trigger === 'click') {
        popover.value.removeEventListener('click', toggle)
      } else {
        popover.value.removeEventListener('mouseenter', open)
        popover.value.removeEventListener('mouseleave', close)
      }
    })
    const toggle = (event) => {
      if (triggerWrapper.value.contains(event.target)) {
        if (visible.value) {
          close()
        } else {
          open()
        }
      }
    }
    const positionContent = () => {
      document.body.appendChild(contentWrapper.value)
      const { width, height, top, left } =
        triggerWrapper.value.getBoundingClientRect()
      const { height: height2 } = contentWrapper.value.getBoundingClientRect()
      const positions = {
        top: {
          left: window.scrollX + left,
          top: window.scrollY + top,
        },
        bottom: {
          left: window.scrollX + left,
          top: window.scrollY + height + top,
        },
        left: {
          left: window.scrollX + left,
          top: window.scrollY + top + (height - height2) / 2,
        },
        right: {
          left: window.scrollX + left + width,
          top: window.scrollY + top + (height - height2) / 2,
        },
      }
      contentWrapper.value.style.left = positions[props.position].left + 'px'
      contentWrapper.value.style.top = positions[props.position].top + 'px'
    }
    const listenToDocument = () => {
      document.addEventListener('click', onClickDocument)
    }
    const onClickDocument = (e) => {
      if (
        !contentWrapper.value.contains(e.target) &&
        !triggerWrapper.value.contains(e.target)
      ) {
        close()
      }
    }
    const close = () => {
      visible.value = false
      document.removeEventListener('click', onClickDocument)
    }
    const open = () => {
      visible.value = true
      nextTick(() => {
        positionContent()
        listenToDocument()
      })
    }

    return {
      visible,
      contentWrapper,
      triggerWrapper,
      popover,
    }
  },
}
</script>

<style lang="scss">
.zeus-popover {
  display: inline-block;
  vertical-align: top;
  position: relative;
  margin: 10px;
}
.content-wrapper {
  position: absolute;
  border-radius: 4px;

  filter: drop-shadow(0 0 3px rgba(0, 0, 0, 0.5));
  padding: 0.5em 1em;
  background: #fff;
  max-width: 20em;
  word-break: break-all;
  &.position-top {
    margin-top: -10px;
    transform: translateY(-100%);
    &::before,
    &::after {
      content: '';
      display: block;
      border: 10px solid transparent;
      position: absolute;
      top: 100%;
      left: 10px;
    }
    &::before {
      top: 100%;
    }
    &::after {
      border-top-color: #fff;
      top: calc(100% - 1px);
    }
  }
  &.position-bottom {
    margin-top: 10px;
    &::before,
    &::after {
      content: '';
      display: block;
      border: 10px solid transparent;
      position: absolute;
      bottom: 100%;
      left: 10px;
    }
    &::before {
      bottom: 100%;
    }
    &::after {
      border-bottom-color: #fff;
      bottom: calc(100% - 1px);
    }
  }
  &.position-left {
    transform: translateX(-100%);
    margin-left: -10px;
    &::before,
    &::after {
      content: '';
      display: block;
      border: 10px solid transparent;
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
    }
    &::before {
      left: 100%;
    }
    &::after {
      border-left-color: #fff;
      left: calc(100% - 1px);
    }
  }
  &.position-right {
    margin-left: 10px;
    &::before,
    &::after {
      content: '';
      display: block;
      border: 10px solid transparent;
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
    }
    &::before {
      right: 100%;
    }
    &::after {
      border-right-color: #fff;
      right: calc(100% - 1px);
    }
  }
}
</style>
